package com.pickup.service.impl;

import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.mongodb.client.result.UpdateResult;
import com.pickup.kafka.provider.KafkaProvider;
import com.pickup.utils.SmsUtils;

import com.pickup.mapper.*;
import com.pickup.netty.enums.NettyResultType;
import com.pickup.netty.message.ChatMessage;
import com.pickup.netty.message.NettyData;
import com.pickup.netty.server.handler.ChatInboundHandler;
import com.pickup.netty.server.session.SessionFactory;
import com.pickup.pojo.ChatMsg;
import com.pickup.pojo.GroupMsg;
import com.pickup.pojo.MyFriends;
import com.pickup.pojo.Users;
import com.pickup.pool.ThreadPoolUtil;
import com.pickup.req.bo.UsersBO;
import com.pickup.req.vo.FriendRequestVO;
import com.pickup.req.vo.MyFriendsVO;
import com.pickup.req.vo.SmsCodeVo;
import com.pickup.req.vo.UsersVO;
import com.pickup.resp.JSONResult;
import com.pickup.service.UserService;
import com.pickup.utils.*;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import com.pickup.utils.SidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UsersMapper userMapper;

    @Autowired
    private UsersMapperCustom usersMapperCustom;

    @Autowired
    private MyFriendsMapper myFriendsMapper;

    @Autowired
    private FriendsRequestMapper friendsRequestMapper;

    @Autowired
    private SidUtil sidUtils;

    @Autowired
    private QRCodeUtils qrCodeUtils;

    @Autowired
    private FastDFSClient fastDFSClient;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private UserUtil userUtil;

    @Autowired
    private SmsUtils smsUtils;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private KafkaProvider kafkaProvider;

    // 判断皮卡号是否存在
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public boolean queryUsernameIsExist(String username) {
        Users user = new Users();
        user.setUsername(username);
        Users result = userMapper.queryUsernameIsExist(username);
        return result != null ? true : false;
    }

    // 判断手机号码是否存在
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public JSONResult queryCellPhoneIsExist(Users user) throws ClientException, ExecutionException, InterruptedException {
        user.setUsername(user.getCellphone());
        Users result = userMapper.queryCellPhoneIsExist(user.getCellphone());
        if (result != null ? true : false) {
            // 1.1 为true则该手机号码已经注册
            return JSONResult.build(500, "该手机号码已经注册账号啦", null);
        } else {
            // 把信息保存在 HashMap
            userUtil.setUserMap(user.getCid(), user);
            kafkaProvider.sendChatMsg("smsMessage", "smsMessage", JsonUtils.objectToJson(user));
            return JSONResult.build(200, "短信发送成功", null);
            //return getVerificationCode(user);
        }
    }

    // 查询用户是否存在/登录
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public JSONResult queryUserForLogin(String keyword, String password) throws Exception {

        Users userResult = userMapper.queryUserForLogin(keyword, password);

        if (userResult == null) {
            return JSONResult.build(500, "没有该用户！", null);
        }
        System.out.println(userResult.getPassword()+"  :  "+MD5Utils.getMD5Str(password));
        if (!userResult.getPassword().equals(MD5Utils.getMD5Str(password))){
            return JSONResult.build(500, "账号或密码不正确！", null);
        }

        // 更新数据，把重要数据截下来，不传给前端
        UsersVO userVO = new UsersVO();
        BeanUtils.copyProperties(userResult, userVO);
        // 更新手机Cid码
        //if (user.getCid() != null) {
        //    Boolean boolUpdateCid = userService.updateCid(new ArrayList<Users>() {{
        //        add(user);
        //    }});
        //}
        return JSONResult.build(200, "登录成功！", userVO);

    }

    // 用户注册
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public JSONResult saveUser(SmsCodeVo smsCodeVo) throws Exception {

        Users userCode = (Users) userUtil.getUserMap(smsCodeVo.getCid());
        String prevCode = null;
        try {
            prevCode = redisTemplate.opsForValue().get("pickup-" + userCode.getCellphone());
        }catch (Exception e){
            JSONResult.build(500, "系统出错！", null);
        }
        // 验证验证码是否正确
        if (prevCode != null && prevCode.equals(smsCodeVo.getSmsCode())) {
            try {
                Users user = new Users();
                // 密码
                user.setPassword(MD5Utils.getMD5Str(userCode.getPassword()));
                // 昵称
                user.setNickname(userCode.getNickname());
                // 账号
                user.setUsername("Pikachu" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
                // 手机号码
                user.setCellphone(userCode.getCellphone());
                // 小头像 M00/00/00/wKgACmIGXRuAMxK6AAQwlq9whmc833_80x80.png
                user.setFaceImage("M00/00/00/CgAIDWIJKjmAKqkiAAQ1sVj4y00990_80x80.png");
                // 大头像
                user.setFaceImageBig("M00/00/00/CgAIDWIJKjmAKqkiAAQ1sVj4y00990.png");
                // IDsearch
                String userId = sidUtils.nextShort();
                // 为每个用户生成一个唯一的二维码
                String qrCodePath = "F:\\NettyImage\\" + userId + "qrcode.png";
                // pikachu_qrcode:[username]
                qrCodeUtils.createQRCode(qrCodePath, "pikachu_qrcode:" + user.getUsername());
                MultipartFile qrCodeFile = FileUtils.fileToMultipart(qrCodePath);
                // 二维码
                String qrCodeUrl = "";
                try {
                    qrCodeUrl = fastDFSClient.uploadQRCode(qrCodeFile);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                user.setQrcode(qrCodeUrl);
                user.setId(userId);
                try {
                    userMapper.insertUserInfo(new ArrayList<Users>() {{ add(user);}});
                    saveFriends(userId, userId);

                }catch (Exception e){
                    e.printStackTrace();
                }
            } catch (Exception e) {
                return JSONResult.build(500, "数据操作失败，请稍后！", null);
            }
            userUtil.delUserMap(smsCodeVo.getCid());
            return JSONResult.build(200, "注册成功！", null);
        } else {
            return JSONResult.build(500, "验证码错误！", null);
        }
    }


    // 修改用户记录
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public JSONResult updateUserInfo(UsersBO userBO) throws Exception {
        // 获取前端传过来的base64字符串, 然后转换为文件对象再上传
        String base64Data = userBO.getFaceData();
        String userFacePath = "F:\\NettyImage\\" + userBO.getUserId() + "userface64.png";
        FileUtils.base64ToFile(userFacePath, base64Data);
        // 上传文件到fastDfs
        MultipartFile faceFile = FileUtils.fileToMultipart(userFacePath);
        assert faceFile != null;
        String url = fastDFSClient.uploadBase64(faceFile);
        // 获取缩略图的url
        String thump = "_80x80.";
        String[] arr = url.split("\\.");
        String thumpImgUrl = arr[0] + thump + arr[1];
        // 更细用户头像
        Users user = new Users();
        user.setId(userBO.getUserId());
        user.setFaceImage(thumpImgUrl);
        user.setFaceImageBig(url);
        Users users = null;
        try {
            userMapper.updateByUserImages(new ArrayList<Users>() {{ add(user);}});
             users = queryUserById(user.getId());
        }catch (Exception e){
            return JSONResult.build(500, "设置失败", users);
        }
        return JSONResult.build(200, "设置成功", users);
    }

    // 获取验证码
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public JSONResult getCode(Users user) throws ClientException, ExecutionException, InterruptedException {
        kafkaProvider.sendChatMsg("smsMessage", "smsMessage", JsonUtils.objectToJson(user));
        return JSONResult.build(200, "短信发送成功", null);
        //  return getVerificationCode(user);
    }

    // 修改用户记录
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public JSONResult updateUserNickname(UsersBO userBO)  {
        Users user = new Users();
        user.setId(userBO.getUserId());
        user.setNickname(userBO.getNickname());

        try {
            userMapper.updateByUserImages(new ArrayList<Users>() {{ add(user);}});
        }catch (Exception e){
            JSONResult.build(500, "设置失败", null);
        }
        return JSONResult.build(200, "设置成功", queryUserById(user.getId()));
    }

    // 查询用户信息
    @Transactional(propagation = Propagation.SUPPORTS)
    public Users queryUserById(String userId) {
        return userMapper.selectByUser(userId);
    }


    // 搜索朋友的前置条件
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public JSONResult preconditionSearchFriends(String myUserId, String friendKeyword) {
        Users user = queryUserInfoByUsername(friendKeyword);
        if (user == null) {
            return JSONResult.build(500, "添加用户为空", null);
        }
        //  搜索账号是你自己
        else if (user.getId().equals(myUserId)) {
            return JSONResult.build(500, "不能添加自己", null);
        }
        //  搜索的朋友是否你的好友
        //  你是对方好友
        if (myFriendsMapper.selectOneByMyFriend(myUserId, user.getId()) != null && myFriendsMapper.selectOneByFriendMy(user.getId(), myUserId) != null) {

            return JSONResult.build(500, "对方已经是你的好友", null);
        }
        Users users = queryUserInfoByUsername(friendKeyword);
        UsersVO userVO = new UsersVO();
        BeanUtils.copyProperties(users, userVO);
        return JSONResult.build(200, "查找成功", userVO);
    }

    // 根据用户名/手机号码查询用户对象
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public Users queryUserInfoByUsername(String keyword) {
        return userMapper.selectOneByUser(keyword);
    }

    // 添加好友请求记录，保存到数据库
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void sendFriendRequest(String myUserId, String friendId) {
        List<String> sendIdList = redisTemplate.opsForList().range(friendId, 0, -1);
        for (String SendId : sendIdList) {
            if (SendId.equals(myUserId)){
                redisTemplate.opsForList().remove(friendId, 0, myUserId);
            }
        }
        Long aLong = redisTemplate.opsForList().rightPush(friendId, myUserId);
    }

    // 查询好友请求
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<FriendRequestVO> queryFriendRequestList(String acceptUserId) {
        List<String> sendIdList = redisTemplate.opsForList().range(acceptUserId, 0, -1);
        List<FriendRequestVO> friendRequestVOS = null;
        if (sendIdList.size() != 0 && sendIdList != null){
            friendRequestVOS = usersMapperCustom.queryByRequestList(sendIdList);
        }
        return friendRequestVOS;
    }

    // 删除好友请求记录
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void deleteFriendRequest(String sendUserId, String acceptUserId) {
        //List<String> sendIdList = redisTemplate.opsForList().range(acceptUserId, 0, -1);
        redisTemplate.opsForList().remove(acceptUserId, 0, sendUserId);
    }

    // 通过好友请求 1. 保存好友 2. 逆向保存好友 3. 删除好友请求记录
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void passFriendRequest(String sendUserId, String acceptUserId) {
        saveFriends(sendUserId, acceptUserId);
        saveFriends(acceptUserId, sendUserId);
        deleteFriendRequest(sendUserId, acceptUserId);
        Channel sendChannel = SessionFactory.getSession().getChannel(sendUserId);
        if (sendChannel != null) {
            // 使用websocket主动推送消息到请求发起者，更新他的通讯录列表为最新
            NettyData nettyData = new NettyData();
            nettyData.setAction(NettyResultType.PullNewContact);
            sendChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(nettyData)));
        }
    }

    // 处理单个好友添加请求，处理两次效率低
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveFriends(String sendUserId, String acceptUserId) {
        MyFriends myFriends = new MyFriends();
        String recordId = sidUtils.nextShort();
        myFriends.setId(recordId);
        myFriends.setMyFriendUserId(acceptUserId);
        myFriends.setMyUserId(sendUserId);
        // new ArrayList<MyFriends>(){{add(myFriends);}}
        myFriendsMapper.insertMyFriends(myFriends);
    }

    // 查询好友列表
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<MyFriendsVO> queryMyFriends(String userId) {
        List<MyFriendsVO> myFirends = usersMapperCustom.queryMyFriends(userId);
        return myFirends;
    }

    // 保存聊天消息到数据库
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public String saveMsg(ChatMessage msg, String signType) {
        ChatMsg msgDB = new ChatMsg();
        String msgId = sidUtils.nextShort();

        msgDB.setId(msgId);
        msgDB.setAcceptUserId(msg.getReceiverId());
        msgDB.setSendUserId(msg.getSenderId());
        msgDB.setCreateTime(new Date());
        msgDB.setSignFlag(signType);
        msgDB.setMsg(msg.getMsg());
        //msgDB.setMsgForByte(msg.getMsgForByte());
        msgDB.setMsgType(msg.getMsgType());
        msgDB.setMsgForByte(msg.getMsgForByte());

        mongoTemplate.save(msgDB);
        return msgId;
    }

    @Override
    public String saveGroupMsg(ChatMessage chatMsg, String signType, String singleId) {
        GroupMsg msgDB = new GroupMsg();
        String msgId = sidUtils.nextShort();

        msgDB.setId(msgId);
        msgDB.setAcceptUserId(chatMsg.getReceiverId());
        msgDB.setSendUserId(chatMsg.getSenderId());
        msgDB.setSingleId(singleId);
        msgDB.setMsgForByte(chatMsg.getMsgForByte());
        msgDB.setMsgType(chatMsg.getMsgType());

        msgDB.setCreateTime(new Date());
        msgDB.setSignFlag(signType);
        msgDB.setMsg(chatMsg.getMsg());
        mongoTemplate.save(msgDB);
        return msgId;
    }

    // 批量签收消息
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void updateMsgSigned(List<String> msgIdList) {
        usersMapperCustom.batchUpdateMsgSigned(msgIdList);
    }

    // 获取未签收用户消息列表
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<ChatMsg> getUnReadMsgList(String userId) {
        Query query = new Query(Criteria.where("acceptUserId").is(userId).and("signFlag").is("0"));
        List<ChatMsg> chatMsgs = mongoTemplate.find(query, ChatMsg.class);
//        FindAndModifyOptions findAndModifyOptions = new FindAndModifyOptions();
//        findAndModifyOptions.upsert(true);
        Update update = Update.update("signFlag", "1");
        UpdateResult updateResult = mongoTemplate.updateMulti(query,update, ChatMsg.class);
        Channel channel = SessionFactory.getSession().getChannel(userId);

        ExecutorService executorPool = new ThreadPoolUtil().getExecutorPool();
        long start  = System.currentTimeMillis();
        if (updateResult.getModifiedCount()==1 && updateResult != null && ChatInboundHandler.users.find(channel.id()) != null){
            for (ChatMsg chatMsg : chatMsgs) {
                try{
                    NettyData nettyDataMsg = new NettyData();
                    nettyDataMsg.setAction(NettyResultType.AcceptSingleChatMsg);
                    ChatMessage nettyMsg = new ChatMessage();
                    nettyMsg.setMsg(chatMsg.getMsg());
                    nettyMsg.setMsgId(chatMsg.getId());
                    nettyMsg.setSenderId(chatMsg.getSendUserId());
                    nettyMsg.setReceiverId(chatMsg.getAcceptUserId());
                    nettyMsg.setMsgType(chatMsg.getMsgType());
                    nettyMsg.setMsgForByte(chatMsg.getMsgForByte());

                    nettyDataMsg.setChatMessage(nettyMsg);
                    nettyDataMsg.setAction(NettyResultType.AcceptSingleChatMsg);
                    System.out.println('1');
                    channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(nettyDataMsg)));
//                    executorPool.execute(()->{
//                    });
                }catch (Exception e){
                    System.out.println("断开了.........");
                }

            }
            long end = System.currentTimeMillis();
            System.out.println((end-start)/1000);
        }
        return chatMsgs;
    }

    // 获取未签收群组消息列表
    @Override
    public void getUnReadMsgGroupList(String userId) {
        Query query = new Query(Criteria.where("singleId").is(userId).and("signFlag").is("0"));
        Update update = Update.update("signFlag", "1");
//        FindAndModifyOptions findAndModifyOptions = new FindAndModifyOptions();
//        findAndModifyOptions.upsert(true);
        List<GroupMsg> groupMsgs = mongoTemplate.find(query, GroupMsg.class);
        UpdateResult updateResult = mongoTemplate.updateMulti(query, update, GroupMsg.class);

        Channel channel = SessionFactory.getSession().getChannel(userId);
        if (updateResult.getModifiedCount()==1 && updateResult != null && ChatInboundHandler.users.find(channel.id()) != null){
            for (GroupMsg groupMsg : groupMsgs) {
                NettyData nettyDataMsg = new NettyData();

                ChatMessage nettyMsg = new ChatMessage();
                nettyMsg.setMsg(groupMsg.getMsg());
                nettyMsg.setMsgId(groupMsg.getId());
                nettyMsg.setSenderId(groupMsg.getSendUserId());
                nettyMsg.setReceiverId(groupMsg.getAcceptUserId());
                nettyMsg.setMsgType(groupMsg.getMsgType());
                nettyMsg.setMsgForByte(groupMsg.getMsgForByte());

                nettyDataMsg.setChatMessage(nettyMsg);
                nettyDataMsg.setAction(NettyResultType.AcceptGroupChatMsg);
                channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(nettyDataMsg)));
            }
        }
    }

    // 重制密码
    @Override
    public JSONResult resetPassword(Users users) {
        try {
            users.setPassword(MD5Utils.getMD5Str(users.getPassword()));
            Boolean aBoolean = userMapper.updateByPassWord(new ArrayList<Users>() {{add(users);}});
            if (aBoolean) {
                return JSONResult.build(200, "密码重制成功！", null);
            }
            return JSONResult.build(500, "密码重制失败！", null);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return JSONResult.build(500, "密码重制失败！", null);
    }

    // 更新手机CID
    @Override
    public Boolean updateCid(ArrayList<Users> user) {
        try {
            return userMapper.updateCid(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 绑定手机号码
    @Override
    public JSONResult bindMobile(SmsCodeVo smsCodeVo) {
        String cellPhone = redisTemplate.opsForValue().get("pickup-" + smsCodeVo.getCellphone());
        if (cellPhone != null && cellPhone.equals(smsCodeVo.getSmsCode())) {
            if (smsCodeVo.getCellPhoneStatus() == 1){
                return JSONResult.build(201, "验证通过！", null);
            }
            if (smsCodeVo.getCellPhoneStatus() == 0){
                Boolean aBoolean = userMapper.bindMobile(smsCodeVo.getUsername(), smsCodeVo.getCellphone());
                if (aBoolean){
                    return JSONResult.build(202, "绑定成功！", null);
                }
            }
        }
        return JSONResult.build(500, "验证失败！", null);
    }



    // 发送短信
    public JSONResult getVerificationCode(Users user) throws ClientException {
        // 1.2 为false则该手机号码未注册
        // 把数据保存在Redis中， 并发一天短信给用户
        String smsCode = RandomStringUtils.randomNumeric(6);
        SendSmsResponse sendSmsResponse = smsUtils.sendSms(user.getCellphone(), smsCode);
        if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
            redisTemplate.opsForValue().set("pickup-" + user.getCellphone(), smsCode, 6, TimeUnit.HOURS);
            return JSONResult.build(200, "短信发送成功", null);
        } else if (Objects.equals(sendSmsResponse.getCode(), "isv.BUSINESS_LIMIT_CONTROL")) {
            return JSONResult.build(500, "今日发送短信到达上限", null);
        } else {
            return JSONResult.build(500, "短信发送失败", null);
        }
    }

}
